package com.celesea.framework.jwt;

import com.alibaba.fastjson.JSON;
import com.celesea.framework.api.HttpKit;
import com.celesea.framework.api.Tip;
import com.celesea.framework.authority.AuthorityPrincipal;
import com.celesea.framework.exception.AuthorizationException;
import com.celesea.framework.util.StringKit;
import com.google.common.collect.Maps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.TimeUnit;

public class JwtFilter extends OncePerRequestFilter {
    private static Logger logger = LoggerFactory.getLogger(JwtFilter.class);
    public static final String X_REQUESTED_WITH = "x-requested-with";
    public static final String URL = "url";

    @Autowired
    private SsoProperties ssoProperties;

    @Autowired
    private JwtProperties properties;

    @Autowired
    private JwtUserDetailService redisUserDetailsService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String loginSessionId = JwtUtil.getSubject(request);
        if (StringKit.isNotEmpty(loginSessionId)) {
            try {
                fetchAuthorityFromRedis(request, response, loginSessionId);
                request.setAttribute("loginSessionId", loginSessionId);
                filterChain.doFilter(request, response);
            } catch (AuthorizationException e) {
                gotoLogin(request, response);
            }
        } else {
            gotoLogin(request, response);
        }
    }

    private void gotoLogin(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String redirectUrl = ssoProperties.getLogin() + "?redirect=" + ssoProperties.getClient();
        if (StringUtils.isEmpty(request.getHeader(X_REQUESTED_WITH))) {
            response.sendRedirect(redirectUrl);
        } else {
            writeJson(response, redirectUrl);
        }
    }

    private void writeJson(HttpServletResponse response, String redirectUrl) throws IOException {
        Tip<Object> tip = HttpKit.unlogin();
        // 放入需要前端重定向的sso地址
        Map<String, Object> map = Maps.newHashMap();
        map.put(URL, redirectUrl);
        tip.setContent(map);
        response.getOutputStream().write(JSON.toJSONString(tip).getBytes());
    }

    private void fetchAuthorityFromRedis(
            HttpServletRequest httpServletRequest,
            HttpServletResponse response, String loginSessionId) throws IOException {
        try {
            AuthorityPrincipal user = redisUserDetailsService.loadUserDetails(loginSessionId);
            user.setJwtToken(JwtCookieUtil.getValue(httpServletRequest));
            SecurityContextHolder.getContext().setAuthentication(
                    new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities()));

            String token = JwtUtil.generateToken(loginSessionId);
            JwtCookieUtil.create(response, token,
                    properties.isSecure(), properties.getMaxAge(), properties.getDomain());

            redisUserDetailsService.updateExpire(loginSessionId, properties.getMaxAge(), TimeUnit.SECONDS);
            // 更新token有效期
        } catch (Exception e) {
            logger.error("获取登录信息失败", e);
            throw new AuthorizationException(e);
        }
    }
}
